package com.imohoo.testbuffers;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.method.ScrollingMovementMethod;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

import com.imohoo.buffers.flat.PathA;
import com.imohoo.buffers.flat.Paths;
import com.imohoo.buffers.json.Points;
import com.imohoo.buffers.protoc.Path1;
import com.google.flatbuffers.FlatBufferBuilder;
import com.google.gson.Gson;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.util.JsonFormat;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by zhaobo on 2017/1/16.
 */

public class TestBuffer extends AppCompatActivity implements View.OnClickListener {
    private TextView tv_show;
    private Button btn_json;
    private Button btn_proto;
    private Button btn_flat;
    private Button btn_build;
    private Spinner spinner_filename;
    private EditText et_times;
    private Button btn_clear;
    private Spinner spinner_filetype;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.test_buffer);
        initViewIDs();
    }

    private void initViewIDs() {
        tv_show = (TextView) findViewById(R.id.tv_show);
        btn_json = (Button) findViewById(R.id.btn_json);
        btn_proto = (Button) findViewById(R.id.btn_proto);
        btn_flat = (Button) findViewById(R.id.btn_flat);
        btn_build = (Button) findViewById(R.id.btn_build);
        btn_json.setOnClickListener(this);
        btn_proto.setOnClickListener(this);
        btn_flat.setOnClickListener(this);
        btn_build.setOnClickListener(this);
        spinner_filename = (Spinner) findViewById(R.id.spinner_filename);
        et_times = (EditText) findViewById(R.id.et_times);
        tv_show.setMovementMethod(ScrollingMovementMethod.getInstance());
        btn_clear = (Button) findViewById(R.id.btn_clear);
        btn_clear.setOnClickListener(this);
        spinner_filetype = (Spinner) findViewById(R.id.spinner_filetype);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_clear:
                tv_show.setText("");
                break;
            case R.id.btn_build:
                build();
                break;
            case R.id.btn_json:
                new Arunnable(new Exec() {
                    @Override
                    public String title() {
                        return "json";
                    }

                    @Override
                    public void run(String filename) {
                        try {
                            Points points = new Gson().fromJson(new InputStreamReader(getAssets().open(getJson(filename))), Points.class);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }).start();
                break;
            case R.id.btn_proto:
                new Arunnable(new Exec() {
                    @Override
                    public String title() {
                        return "proto";
                    }

                    @Override
                    public void run(String filename) {
                        try {
                            Path1.Paths paths = Path1.Paths.parseFrom(new FileInputStream(getExtProtocs(filename)));
                        } catch (Exception ex) {
                            ex.printStackTrace();
                        }
                    }
                }).start();
                break;
            case R.id.btn_flat:
                new Arunnable(new Exec() {
                    @Override
                    public String title() {
                        return "flat";
                    }

                    @Override
                    public void run(String filename) {
                        Paths paths = getPaths(getFileName());
                        if (paths != null) {
                            int count = paths.pathsLength();
                            for (int i = 0; i < count; i++) {
                                paths.paths(0).lat();
                            }
                        }
                    }
                }).start();
                break;
        }
    }

    interface Exec {
        String title();

        void run(String filename);
    }

    class Arunnable extends Thread {
        Exec exec;

        public Arunnable(Exec exec) {
            this.exec = exec;
        }

        @Override
        public void run() {
            int count = 500;
            String fileName = getFileName();
            try {
                count = Integer.parseInt(et_times.getText().toString());
            } catch (Exception ex) {
                showToast("次数设置有误,使用500次计算");
                ex.printStackTrace();
            }
            List<Long> list = new ArrayList<>();
            for (int i = 0; i < count; i++) {
                long begin = System.currentTimeMillis();
                // TODO dosome
                exec.run(fileName);
                long end = System.currentTimeMillis();
                list.add(end - begin);
            }

            // 平均
            long allTime = 0;
            for (long l : list) {
                allTime += l;
            }
            showText(String.format("%1$s %2$s %3$s次运算", exec.title(), fileName, count));
            showText(String.format("%1$s 平均用时%2$s毫秒", exec.title(), allTime / count));

            // 最大
            long maxTime = 0;
            for (long l : list) {
                if (l > maxTime) {
                    maxTime = l;
                }
            }
            showText(String.format("%1$s 最多用时%2$s毫秒", exec.title(), maxTime));

            // 最小
            long minTime = Long.MAX_VALUE;
            for (long l : list) {
                if (l < minTime) {
                    minTime = l;
                }
            }
            showText(String.format("%1$s 最少用时%2$s毫秒", exec.title(), minTime));
        }
    }

    private void showToast(final String text) {
        tv_show.post(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(TestBuffer.this, text, Toast.LENGTH_SHORT).show();
            }
        });
    }

    private void showText(final String text) {
        tv_show.post(new Runnable() {
            @Override
            public void run() {
                tv_show.append(text);
                tv_show.append("\n");
            }
        });
    }

    private String getFileName() {
        return spinner_filename.getSelectedItem().toString();
    }

    private String getFlat(String fileName) {
        return "flat/" + fileName + ".bin";
    }

    private String getJson(String fileName) {
        return "json/" + fileName + ".json";
    }

    private String getExtProtocs(String fileName) {
        return getExternalFilesDir("protocs") + "/" + fileName + ".data";
    }

    private String getExtProtoc(String fileName) {
        return getExternalFilesDir("protoc") + "/" + fileName + ".data";
    }

    private String getExtJson(String fileName) {
        return getExternalFilesDir("json") + "/" + fileName + ".json";
    }

    private String getExtFlat(String fileName) {
        return getExternalFilesDir("flat") + "/" + fileName + ".bin";
    }

    private void buildProtos(String fileName) {
        try {
            File file = new File(getExtProtocs(fileName));
            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
            }
            FileOutputStream fos = new FileOutputStream(file);
            JsonFormat.Parser printer = JsonFormat.parser();
            Path1.Paths.Builder builder = Path1.Paths.newBuilder();
            printer.merge(new InputStreamReader(getAssets().open(getJson(fileName))), builder);
            builder.build().writeTo(fos);
            fos.flush();
            fos.close();
        } catch (InvalidProtocolBufferException e) {
            e.printStackTrace();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private Points points;

    private void build() {
        try {
            points = new Gson().fromJson(new InputStreamReader(getAssets().open(getJson(getFileName()))), Points.class);
        } catch (IOException e) {
            e.printStackTrace();
        }

        String filetype = spinner_filetype.getSelectedItem().toString();
        if ("all protoc".equals(filetype)) {
            for (int i = 0; i < spinner_filename.getAdapter().getCount(); i++) {
                buildProtos(spinner_filename.getAdapter().getItem(i).toString());
            }
        } else if ("json".equals(filetype)) {
            new Arunnable(new Exec() {
                @Override
                public String title() {
                    return "json";
                }

                @Override
                public void run(String filename) {
                    new Gson().toJson(points);
                }
            }).start();
        } else if ("protoc".equals(filetype)) {
            new Arunnable(new Exec() {
                @Override
                public String title() {
                    return "protoc";
                }

                @Override
                public void run(String filename) {
                    try {
                        Path1.Paths.Builder builder = Path1.Paths.newBuilder();
                        for (com.imohoo.buffers.json.Point point : points.getPaths()) {
                            builder.addPaths(Path1.PathA.newBuilder()
                                    .setA(point.getA())
                                    .setS(point.getS())
                                    .setLat(point.getLat())
                                    .setLon(point.getLon())
                                    .setTime(point.getTime())
                            ).build();
                        }
                        builder.build().writeTo(new FileOutputStream(getExtProtoc(getFileName())));
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                }
            }).start();
        } else if ("flat".equals(filetype)) {
            new Arunnable(new Exec() {
                @Override
                public String title() {
                    return "flat";
                }

                @Override
                public void run(String filename) {
                    try {
                        FlatBufferBuilder fbb = new FlatBufferBuilder();
// 单个
//                        com.example.json.Point point = points.getPaths().get(0);
//                        int off = PathA.createPathA(fbb, point.getA(), point.getS(), point.getLat(), point.getLon(), point.getTime());
//                        fbb.finish(off);

                        // 多个
                        int count = points.getPaths().size();
                        int[] list = new int[count];
                        for (int i = 0; i < count; i++) {
                            com.imohoo.buffers.json.Point point = points.getPaths().get(i);
                            int off = PathA.createPathA(fbb, point.getA(), point.getS(), point.getLat(), point.getLon(), point.getTime());
                            list[i] = off;
                        }
                        int off = Paths.createPathsVector(fbb, list);
                        fbb.finish(off);
                        try {
                            DataOutputStream os = new DataOutputStream(new FileOutputStream(getExtFlat(filename)));
                            os.write(fbb.dataBuffer().array(), fbb.dataBuffer().position(), fbb.offset());
                            os.close();
                        } catch (java.io.IOException e) {
                            System.out.println("FlatBuffers test: couldn't write file");
                            return;
                        }
                        System.out.println();
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                }
            }).start();
        }
    }

    private Paths getPaths(String filename) {
        try {
            File file = new File(getExtFlat(filename));
            if (file.exists()) {
                InputStream is = new FileInputStream(file);
                byte[] data = new byte[is.available()];
                is.read(data);
                is.close();
                ByteBuffer bb = ByteBuffer.wrap(data);
                Paths paths = Paths.getRootAsPaths(bb);
                return paths;
            }
            InputStream is = getAssets().open(getFlat(filename));
            byte[] data = new byte[is.available()];
            is.read(data);
            is.close();
            ByteBuffer bb = ByteBuffer.wrap(data);
            Paths paths = Paths.getRootAsPaths(bb);
            return paths;
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }
}
